import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;

export class UIWatcher {
private _data:Object=null;
private _key:string='';
private _target:any=null;
private _cb:Function=null;
private _comp:Component=null;
    constructor(data:Object,key:string,target:any,cb:Function,comp?:Component){
        this._data = data;
        this._key = key;
        this._target = target;
        this._cb = cb;
        this._comp=comp;

        //需要知道data的key属性的dep是否已经有了，如果有了，把这个监听直接加入到key的dep中。
        //如果没有？直接创建一个dep，把当前监听者加入进去。
        //这里就可以获取到对应的dep，拿到这个dep之后，要把watcher对象加入到这个dep的数组中。 
        UIDepPool.instance.getDep(data,key).addWatcher(this);
    }
    notify(oldValue:any,newValue:any){
        //告诉跟这个watcher关联的UI去更新。 ==》 ui，某个毁掉函
        //this.关联UI.更新UI(oldValue,newValue);
        let args = [];
        for(let value of Array.from(arguments)){
            args.push(value);
        }
        this._comp&&args.push(this._comp);
        this._cb.apply(this._target,args);
    }
    isEquals(watcherIn:UIWatcher){
        return this._data == watcherIn._data
            && this._key == watcherIn._key
            && this._target == watcherIn._target
            && this._cb == watcherIn._cb;
    }
    removeSelf(){
        let dep=UIDepPool.instance.getDep(this._data,this._key);
        dep.removeWatcher(this);
        
    }
}
class UIDep{
    private _data:object=null;
    get data(){
        return this._data;
    }
    private _key:string='';
    get key(){
        return this._key;
    }
    private _arrWatcher:UIWatcher[]=[];
    constructor(data:Object,key:string){
        this._data = data;
        this._key = key;

        //直接开始监听。 data里面key属性。
        this._definedProperty(this._data,this._key);
    }
    private _definedProperty(data:object,key:string){
        let newValue=data[key];
        let self=this;
        Object.defineProperty(data,key,{
            set(value:any){
                let oldValue=newValue;
                newValue=value;
                self._notify(oldValue,newValue);
            },
            get(){
                return newValue;
            }
        })
    }
    private _notify(oldValue:any,newValue:any){
        for(let watcher of this._arrWatcher){
            watcher.notify.apply(watcher,arguments);
        }
    }
    addWatcher(watcherIn:UIWatcher){
        for(let wather of this._arrWatcher){
            if(watcherIn.isEquals(wather)){
                return;
            }
        }
        this._arrWatcher.push(watcherIn);
    }
    isContain(data:Object,key:string){
        return this._data == data&&this._key == key;
    }
    isEquals(dep:UIDep){
        return this._data == dep.data&&this._key == dep.key;
    }
    removeWatcher(watcherIn:UIWatcher){
        for(let i=0;i<this._arrWatcher.length;i++){
            if(this._arrWatcher[i].isEquals(watcherIn)){
                this._arrWatcher.splice(i,1);
                break;
            }
        }
        if(this._arrWatcher.length<=0){
            UIDepPool.instance.removeDep(this);
        }

    }


}
class UIDepPool{
    private static _instance:UIDepPool=null;
    static get instance(){
        if(!this._instance){
            this._instance=new UIDepPool();
        }
        return this._instance;
    }
    private _arrDep:UIDep[] = [];
    constructor(){

    }
    getDep(data:object,key:string){
        for(let depin of this._arrDep){
            if(depin.isContain(data,key)){
                return depin;
            }
        }
        let dep=new UIDep(data,key);
        this._arrDep.push(dep);
        return dep;
    }
    removeDep(dep:UIDep){
        for(let i=0;i<this._arrDep.length;i++){
            if(this._arrDep[i].isEquals(dep)){
                this._arrDep.splice(i,1);
                return;
            }
        }
    }

}


